package org.budo.mongo.jdbc.driver.parser.jsqlparser;

import java.util.List;

import org.budo.jdbc.driver.exception.BudoJdbcDriverNotSupportedException;
import org.budo.jdbc.driver.exception.SubSelectNotSupportedException;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.update.Update;

/**
 * @author li
 */
public class MongoJSqlParserUtil {
    public static String[] getSelectColumnNames(Statement statement) {
        List<SelectItem> selectItems = getSelectItems(statement);

        String[] columnNames = new String[selectItems.size()];

        for (int i = 0; i < selectItems.size(); i++) {
            SelectItem selectItem = selectItems.get(i);
            columnNames[i] = selectItem.toString();
        }

        return columnNames;
    }

    public static List<SelectItem> getSelectItems(Statement statement) {
        if (!(statement instanceof Select)) {
            throw new BudoJdbcDriverNotSupportedException("#38 statement=" + statement + ", type=" + statement.getClass());
        }

        Select select = (Select) statement;
        SelectBody selectBody = select.getSelectBody();
        if (!(selectBody instanceof PlainSelect)) {
            throw new BudoJdbcDriverNotSupportedException("#38 statement=" + statement + ", type=" + statement.getClass());
        }

        PlainSelect plainSelect = (PlainSelect) selectBody;
        return plainSelect.getSelectItems();
    }

    protected static String getTableName(Table table) {
        if (null == table) {
            return null;
        }

        String tableName = table.getName();
        int len = tableName.length();
        if (tableName.charAt(0) == '`' && tableName.charAt(len - 1) == '`') {
            return tableName.substring(1, len - 1);
        }

        return tableName;
    }

    protected static boolean isCountStatement(PlainSelect plainSelect) {
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        if (null == selectItems || 1 != selectItems.size()) {
            return false;
        }

        SelectItem selectItem = selectItems.get(0);
        if (!(selectItem instanceof SelectExpressionItem)) {
            return false;
        }

        SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
        Expression expression = selectExpressionItem.getExpression();
        if (!(expression instanceof Function)) {
            return false;
        }

        Function function = (Function) expression;
        String functionName = function.getName();
        if (!"COUNT".equalsIgnoreCase(functionName)) {
            return false;
        }

        if (function.isAllColumns()) {
            return true;
        }

        ExpressionList expressionList = function.getParameters();
        if (null == expressionList) {
            return false;
        }

        List<Expression> expressions = expressionList.getExpressions();
        if (1 != expressions.size()) {
            return false;
        }

        Expression expressionInFunction = expressions.get(0);
        if (expressionInFunction instanceof LongValue) {
            return true;
        }

        return false;
    }

    protected static String getColumnName(Expression expression) {
        if (expression instanceof Column) {
            Column column = (Column) expression;
            String columnName_0 = column.getColumnName();
            String columnName_1 = columnName_0.replace("`", "");
            return columnName_1;
        }

        throw new BudoJdbcDriverNotSupportedException("#33 expression=" + expression + ", expression.type=" + expression.getClass());
    }

    /**
     * @see org.budo.mongo.jdbc.driver.parser.jsqlparser.JSqlParserMongoStatementParser#mongoAggregateStatement(String,Statement,PlainSelect)
     */
    protected static boolean isAggregateStatement(PlainSelect plainSelect) {
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        if (null == selectItems || 1 != selectItems.size()) {
            return false;
        }

        SelectItem selectItem = selectItems.get(0);
        if (!(selectItem instanceof SelectExpressionItem)) {
            return false;
        }

        SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
        Expression expression = selectExpressionItem.getExpression();
        if (!(expression instanceof Function)) {
            return false;
        }

        Function function = (Function) expression;
        String functionName = function.getName();
        if (!("MAX".equalsIgnoreCase(functionName) //
                || "MIN".equalsIgnoreCase(functionName) //
                || "AVG".equalsIgnoreCase(functionName) //
                || "SUM".equalsIgnoreCase(functionName))) {
            return false;
        }

        ExpressionList expressionList = function.getParameters();

        if (null == expressionList) {
            return false;
        }

        List<Expression> expressions = expressionList.getExpressions();
        if (1 != expressions.size()) {
            return false;
        }

        Expression expressionInFunction = expressions.get(0);
        if (expressionInFunction instanceof Column) {
            return true;
        }

        return false;
    }

    protected static Function getSelectFunction(PlainSelect plainSelect) {
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        SelectItem selectItem = selectItems.get(0);
        SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
        Expression expression = selectExpressionItem.getExpression();
        return (Function) expression;
    }

    protected static Column getSelectFunctionParameter(PlainSelect plainSelect) {
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        SelectItem selectItem = selectItems.get(0);
        SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
        Expression expression = selectExpressionItem.getExpression();
        Function function = (Function) expression;
        ExpressionList expressionList = function.getParameters();

        List<Expression> expressions = expressionList.getExpressions();
        Expression expressionInFunction = expressions.get(0);
        return (Column) expressionInFunction;
    }

    protected static Table getUpdateTable(String sql, Update update) {
        List<Table> tables = update.getTables();
        if (tables.size() > 1) {
            throw new BudoJdbcDriverNotSupportedException("#163 sql=" + sql + ", 不支持同时更新多个表");
        }

        Table table = tables.get(0);
        return table;
    }

    protected static Table getFromTable(Statement statement) {
        if (statement instanceof Select) {
            Select select = (Select) statement;
            SelectBody selectBody = select.getSelectBody();
            if (selectBody instanceof PlainSelect) {
                PlainSelect plainSelect = (PlainSelect) selectBody;
                FromItem fromItem = plainSelect.getFromItem();
                if (null == fromItem) {
                    return null;
                }

                if (fromItem instanceof Table) {
                    return (Table) fromItem;
                }

                if (fromItem instanceof SubSelect) {
                    throw new SubSelectNotSupportedException("#180 SubSelect not supported, statement=" + statement);
                }
            }
        }

        throw new BudoJdbcDriverNotSupportedException("#185 statement=" + statement + ", type=" + statement.getClass());
    }

    protected static boolean isFindStatement(PlainSelect plainSelect) {
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        for (SelectItem selectItem : selectItems) {
            if (!(selectItem instanceof SelectExpressionItem)) {
                continue;
            }

            SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
            Expression expression = selectExpressionItem.getExpression();
            if (!(expression instanceof Column || expression instanceof SubSelect)) {
                throw new BudoJdbcDriverNotSupportedException("#213 expression=" + expression + ", expression.type=" + expression.getClass() + " plainSelect=" + plainSelect);
            }
        }

        return true;
    }

    protected static boolean hasSubSelect(PlainSelect plainSelect) {
        FromItem fromItem = plainSelect.getFromItem();
        if (fromItem instanceof SubSelect) {
            return true;
        }

        return false;
    }

    // SHOW CREATE TABLE `receptionRecordFilter`
    public static String getTableNameWhenShow(String sql) {
        return sql.replace("SHOW CREATE TABLE ", "").replace("`", "");
    }
}
